var Storage = {
	curVer: 1,
	
	meta:{},
	
	registerClass:function(className){
		_global[className].prototype._class = className;
	},
	
	registerObj: function(className, property, defaultVal){
		with(this){
			if (!(className in meta)) meta[className] = {};
			meta[className][property] = defaultVal;
		}
	},
	
	saveObj:function(obj){
		with(this){
			if (!(obj._class in meta)){
				return obj;
			}
			var classMeta = meta[obj._class];
			var ret = {};
			for(prop in classMeta){
				ret[prop] = saveObj(obj[prop]);
			}
			return ret;
		}
	},
	
	loadObj:function(obj, data){
		with(this){
			if (obj._class in meta){
				for (prop in data){
					if (obj[prop]._class in meta){
						loadObj(obj[prop], data[prop]);
					}else{
						obj[prop] = data[prop];
					}
				}
			}else{
				obj = data;
			}
		}
	},
	
	save:function(obj){
		with(this){
			localStorage['ver'] = curVer;
			localStorage["doc"] = JSON.stringify(saveObj(obj));
			for (var feature_name in _data){
				localStorage[feature_name] = JSON.stringify(
					_data[feature_name].save());
			}
		}
	},
	
	load:function(obj){
		with(this){
			if ("doc" in localStorage){
				loadObj(obj, JSON.parse(localStorage["doc"]));
				if ('invalidate' in obj)
					obj.invalidate();
			}
			for (var feature_name in _data){
				if (feature_name in localStorage)
					_data[feature_name].load(
						JSON.parse(localStorage[feature_name]));
			}
		}
	},
	
	_data:{},
	registerStorage:function(feature_name, save_func, load_func){
		this._data[feature_name] = {
			save:save_func,
			load:load_func
		}
	}
}

var Revision = {
	enable:true,
	undoFuncs:[],
	redoFuncs:[],
	applyAction:function(undoFunc){
		if (!this.isCombining) undoFunc.redo();
		with(this){
			if (this.isCombining){
				combineF.push(undoFunc);
			}else{
				redoFuncs = [];//clear the redo
				if (undoFuncs.length>0){
					var lastUndo = undoFuncs[undoFuncs.length-1];
					if (lastUndo.id && lastUndo.id == undoFunc.id){//combine two undos
						lastUndo.redo = undoFunc.redo;
						//_log.innerHTML = 'join.'+undoFuncs.length;
						return;
					}
				}
				undoFuncs.push(undoFunc);
			}
			//_log.innerHTML = 'push.'+undoFuncs.length;
		}			
	},
	undo:function(){
		with(this){
			if (undoFuncs.length>0){
				var func = undoFuncs.pop();
				func();
				redoFuncs.push(func);
			}
			//_log.innerHTML = undoFuncs.length+'.'+redoFuncs.length;
		}
	},
	redo:function(){
		with(this){
			if (redoFuncs.length>0){
				var func = redoFuncs.pop();
				func.redo();
				undoFuncs.push(func);
			}
			//_log.innerHTML = undoFuncs.length+'.'+redoFuncs.length;
		}
	},
	
	isCombining:false,
	combineF:null,
	combine:function(undo_id, func){
		with(this){
			isCombining = true;
			combineF = [];
			func();
			isCombining = false;
			var myComb = combineF;
			var newUndo = function(){
				var tarr = [];
				myComb.forEach(function(f){//reverse the order
					tarr.push(f);
				});
				while(tarr.length>0){
					var ff = tarr.pop();
					ff();
				}
			}
			newUndo.redo = function(){
				myComb.forEach(function(f){
					f.redo();
				});
			}
			newUndo.id = undo_id;
			applyAction(newUndo);
		}
	}
}

function _clone(obj){
	if (obj && obj instanceof Object){
		if (obj instanceof WebObj)
			return obj;
		var newObj = (obj instanceof Array) ? [] : {};
		for (i in obj) {
			//if (i == 'clone') continue;
			if (obj[i] && obj[i] instanceof Object) {
				newObj[i] = _clone(obj[i]);
			} else newObj[i] = obj[i];
		}
		return newObj;
	}else
		return obj;
}

//logpush('Finish processing Storage');